﻿// The Nova Project by Ken Beckett.
// Copyright (C) 2007-2012 Inevitable Software, all rights reserved.
// Released under the Common Development and Distribution License, CDDL-1.0: http://opensource.org/licenses/cddl1.php

using System;
using System.Configuration;
using System.Reflection;

using Nova.CodeDOM;

namespace Nova
{
    /// <summary>
    /// Configuration class for Nova classes.
    /// </summary>
    public class Configuration : ConfigurationSection
    {
        /// <summary>
        /// Determines if non-default configuration settings should be logged.
        /// </summary>
        public static bool LogSettings = true;

        /// <summary>
        /// A key-value collection for all Nova settings (works like 'appSettings', but in a separate configSection).
        /// </summary>
        [ConfigurationProperty("", IsRequired = true, IsDefaultCollection = true)]
        public KeyValueConfigurationCollection Settings
        {
            get { return (KeyValueConfigurationCollection)this[""]; }
            set { this[""] = value; }
        }

        /// <summary>
        /// Load the settings from the config file.
        /// </summary>
        public static void LoadSettings()
        {
            string namespaceName = typeof(Configuration).Namespace;
            try
            {
                // Use reflection to set any static fields on any types.  Use the calling assembly so that this method
                // can be called for different assemblies (such as Nova.CodeDOM.dll and Nova.UI.dll).
                Assembly assembly = Assembly.GetCallingAssembly();
                Configuration config = (Configuration)ConfigurationManager.GetSection(namespaceName);
                if (config == null)
                    Log.DetailWriteLine("Nova.Configuration: WARNING: No '" + namespaceName + "' section found in .config file.");
                else
                {
                    foreach (string key in config.Settings.AllKeys)
                    {
                        // Split the key into a type name (with optional namespace prefix) and field name
                        int typeNameIndex = key.LastIndexOf('.');
                        if (typeNameIndex < 0)
                            LogMessage("Invalid key format '" + key + "' - must be 'typename.fieldname' or 'namespace.typename.fieldname'!", MessageSeverity.Error);
                        else
                        {
                            string typeName = key.Substring(0, typeNameIndex);
                            string fieldName = key.Substring(typeNameIndex + 1);

                            // Get the type using reflection - just ignore any types that aren't found, so that
                            // settings can exist in the file for different assemblies.
                            string fullTypeName = namespaceName + "." + typeName;
                            Type type = assembly.GetType(fullTypeName);
                            if (type != null)
                            {
                                // Get the field from the type (assume it's public and static)
                                FieldInfo fieldInfo = type.GetField(fieldName, BindingFlags.Public | BindingFlags.Static);
                                if (fieldInfo == null)
                                    LogMessage("Type '" + typeName + "' doesn't have a public static field '" + fieldName + "'!", MessageSeverity.Error);
                                else
                                {
                                    // Set the static field to the value from the config file
                                    Type fieldType = fieldInfo.FieldType;
                                    object value = config.Settings[key].Value;
                                    if (fieldType.IsEnum && value is string)
                                    {
                                        if (fieldType == typeof(Log.Level))
                                        {
                                            Log.Level enumValue;
                                            if (Enum.TryParse((string)value, true, out enumValue))
                                                value = enumValue;
                                        }
                                    }
                                    fieldInfo.SetValue(null, Convert.ChangeType(value, fieldType));
                                    if (LogSettings)
                                        LogMessage(typeName + "." + fieldName + " = " + value, MessageSeverity.Information);
                                }
                            }
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                LogException(ex, "loading");
            }
        }

        /// <summary>
        /// Log the specified text message with the specified severity level.
        /// </summary>
        public static void LogMessage(string message, MessageSeverity severity, string toolTip)
        {
            string prefix = (severity == MessageSeverity.Error ? "ERROR: " : (severity == MessageSeverity.Warning ? "Warning: " : ""));
            if (severity == MessageSeverity.Error || severity == MessageSeverity.Warning || Log.LogLevel >= Log.Level.Detailed)
                Log.WriteLine(prefix + "Configuration file: " + message, toolTip != null ? toolTip.TrimEnd() : null);
        }

        /// <summary>
        /// Log the specified text message with the specified severity level.
        /// </summary>
        public static void LogMessage(string message, MessageSeverity severity)
        {
            LogMessage(message, severity, null);
        }

        /// <summary>
        /// Log the specified exception and message.
        /// </summary>
        public static string LogException(Exception ex, string message)
        {
            return Log.Exception(ex, message + " configuration file");
        }
    }
}
